package eval.engine.cplex.InitialCplexCode;

import ilog.opl.IloCustomOplDataSource;
import ilog.opl.IloOplFactory;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.util.logging.Logger;

/**
 * @author Yosef Moatti
 * 
 * moatti@il.ibm.com
 *         
 */
public class CplexDataSource extends IloCustomOplDataSource {


	final static Class<CplexDataSource> sCLASS = CplexDataSource.class;
	final static String CLASSNAME = sCLASS.getName(); 
	final static Logger sLOGGER = Logger.getLogger("vsilogger.debug");
	final static Logger essentialLogger = Logger.getLogger("vsilogger.debug");
	
	// All the names here should be the names of the OPL Data Model data
	// members!
	static final String CPLEX_PROPERTY_PREFIX      = "cplex.props.";

	private static final String CPLEX_DATA_FILE_PREFIX = " CPLEX DATA INPUT\n";
	/**
	 * the maximal load of a VM so that it will load balance with a 100% loaded VM 
	 * by exchanging a single standard VM between them.
	 */
	static final String RELOCATED_LOAD = "relocated.load";  
	/**
	 * The maximal load of a standard host so that it will load balance a standard VM with a standard host loaded at 0%
	 */
	static final String UPPER_BOUND_NORMAL_RANGE  = "upper.bound.normal.range";   
	/**
	 * The ratio between a standard VM and a standard host
	 */
	static final String STANDARD_MINIMAL_UTILIZATION_GAP  = "minimal.utilization.gap";   

	static final float DEFAULT_RELOCATED_LOAD           = 0.05f;   // between 0.0 and 1.0
	static final float DEFAULT_UPPER_BOUND_NORMAL_RANGE = 0.60f;   // between 0.0 and 1.0
	static final float DEFAULT_MINIMAL_UTILIZATION_GAP  = 0.20f;   // between 0.0 and 1.0

	// should be at least 2:
	private static final String CPLEX_OPL_INPUT_HEADER = "Input data for OPL, jobId=";

	private Boolean useCplexJNI = null;  // if true data will be passed to CPLEX thru multiple calls of the JNI interface else it will be passed as a file 
	private StringBuffer sbFile = null;

	CplexDataSource(/*Context planContext*/) {
		super(new IloOplFactory());
	}

	/**
	 * @Constructor
	 * @param oplFactory
	 * @param cplexInputString
	 */
	public CplexDataSource(final IloOplFactory oplFactory, String cplexInputString) {
		super(oplFactory);

		// if useCplexJNI is false the input to CPLEX will consist of an input.dat file, the multiple JNI will not be invoked 
		useCplexJNI = false; // new Boolean(props.getProperty((CPLEX_PROPERTY_PREFIX + USE_CPLEX_JNI)).trim());

		sbFile = new StringBuffer(cplexInputString);

	}

	/*
	 * (non-Javadoc)
	 * 
	 * Automatically invoked by the CPLEX engine
	 * 
	 * @see ilog.opl.IloCustomOplDataSource#customRead()
	 */
	@Override
	public void customRead() {
		writeDataFile();
	}

	private void writeDataFile() {
		
		if (isCplexInputLoggable()) {
			StringBuffer headerSb = new StringBuffer();
			headerSb.append(CPLEX_OPL_INPUT_HEADER);
			
			essentialLogger.log(CplexAdapter.cplexInputJournalLevel, headerSb.toString() + sbFile.toString());
		}

		if (CplexAdapter.COLLECT_CPLEX_FILES_TO_FILE) {
			// In any case, write content of input file to log:
			sLOGGER.finest(CPLEX_DATA_FILE_PREFIX + sbFile.toString());

			// Write input file to file system if needed:
			if (useCplexJNI) {
				sLOGGER.info("Using cplex JNI multiple invocations mode");
			} else {
				sLOGGER.info("Using cplex all in one file JNI invocations mode");
				String directoryPath = CplexAdapter.DIRECTORY_PATH;
				if (directoryPath != null && directoryPath.length()>0) {
				  long jobId = System.currentTimeMillis();
				  String thePath = directoryPath + generateNewDataFileName(jobId);
				  stringToFile(thePath, /*translationTables + */ sbFile.toString());
				}
			}
		} 
	}

	/**
	 * @param filePath
	 * @param fileContent
	 */
	public static void stringToFile(String filePath, String fileContent) {
		try{
			FileWriter fstream = new FileWriter(filePath);
			BufferedWriter out = new BufferedWriter(fstream);
			out.write(fileContent);
			out.close();
		}catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public String getDataString() {
		return sbFile.toString();
	}

	/**
	 * Returns true if JNI multiple invocations are to be used to send input data to OPL model
	 */
	public boolean isUseCplexJNI() {
		return useCplexJNI;
	}

	/**
	 * @return a unique Cplex input data file name based on the jobId and system time 
	 */
	public String generateNewDataFileName() {
		return generateNewDataFileName(0);
	}
	
	/**
	 * @return a unique Cplex input data file name based on the jobId and system time 
	 */
	public String generateNewDataFileName(long jobId) {
		String jobIdString = "";
		if (jobId != 0) {
			jobIdString = "_" + jobId;
		}
	  	String fullPath = CplexAdapter.FILE_BASE_NAME + "_" + CplexPlacementModel.getFileIndexString() + jobIdString + "_" + System.currentTimeMillis();
		sLOGGER.info(" latest file is " + fullPath);
		
		return fullPath;
	}

	private boolean isCplexInputLoggable() {
		return true;
	}

	
}
